//
//  HHMovedLayout.swift
//  HHSwift
//
//  Created by 华宏 on 2020/1/27.
//  Copyright © 2020 huahong. All rights reserved.
//

import UIKit

//MARK: - 协议
@objc protocol HHMovedLayoutDataSource:UICollectionViewDataSource {
    
    @objc optional  func collectionView(_ collectionView:UICollectionView?,willMove sourceIndexPath:IndexPath,destinationIndexPath:IndexPath) -> Void
    
    @objc optional  func collectionView(_ collectionView:UICollectionView?,didMove sourceIndexPath:IndexPath,destinationIndexPath:IndexPath) -> Void
    
    @objc optional  func collectionView(_ collectionView:UICollectionView?,canMove indexPath:IndexPath) -> Bool
    
    @objc optional  func collectionView(_ collectionView:UICollectionView?,canMove sourceIndexPath:IndexPath,destinationIndexPath:IndexPath) -> Bool

    
}

@objc protocol HHMovedLayoutDelegate:UICollectionViewDelegateFlowLayout {
  
    
    @objc optional  func collectionView(_ collectionView:UICollectionView?,willBeginDragging layout:UICollectionViewLayout,indexPath:IndexPath) -> Void
    
    @objc optional  func collectionView(_ collectionView:UICollectionView?,didBeginDragging layout:UICollectionViewLayout,indexPath:IndexPath) -> Void

    @objc optional  func collectionView(_ collectionView:UICollectionView?,willEndDragging layout:UICollectionViewLayout,indexPath:IndexPath) -> Void

    @objc optional  func collectionView(_ collectionView:UICollectionView?,didEndDragging layout:UICollectionViewLayout,indexPath:IndexPath) -> Void


}


class HHMovedLayout: UICollectionViewFlowLayout,UIGestureRecognizerDelegate {

    //MARK: - 属性
    
    var delegate:HHMovedLayoutDelegate? {
       
        get{
            return self.collectionView?.delegate as? HHMovedLayoutDelegate
        }
    }
    
    var dataSource:HHMovedLayoutDataSource? {
       
        get{
            return self.collectionView?.dataSource as? HHMovedLayoutDataSource
        }
    }
    
    var longPressGesture:UILongPressGestureRecognizer!
    var movingIndexPath:IndexPath?
    var snapshotView:UIView!
    var originCellCenter:CGPoint!
    
    //MARK: - 构造函数
    override init() {
        super.init()
        
        addObserver()
        
        addGesture()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    //MARK: - 重写 UICollectionViewLayout 方法
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let array = super.layoutAttributesForElements(in: rect)
        
        guard let attributesArr = array else { return nil }
        
        for attributes in attributesArr {
            if attributes.representedElementCategory == .cell {
                attributes.isHidden = (attributes.indexPath.item == movingIndexPath?.item)
            }
        }
        
        return array
        
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        
        let attributes = super.layoutAttributesForItem(at: indexPath)
        if attributes?.representedElementCategory == .cell {
            attributes?.isHidden = (attributes?.indexPath == movingIndexPath)
        }
        
        return attributes
        
    }
    
    //MARK: - 添加/移除监听
    func addObserver() {
        self.addObserver(self, forKeyPath:"collectionView", options: .new, context: nil)
        removeGesture()
    }
    
    deinit {
        self.removeObserver(self, forKeyPath: "collectionView")
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        if keyPath == "collectionView" {
            if self.collectionView != nil {
                addGesture()
            }else{
                removeGesture()
            }
        }
    }
    
    //MARK: - 添加、移除长按手势
    func addGesture() {
      
        self.collectionView?.isUserInteractionEnabled = true
        longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction))
        longPressGesture.cancelsTouchesInView = false
        longPressGesture.minimumPressDuration = 0.1
        longPressGesture.delegate = self
        
        guard let gestureRecognizers = self.collectionView?.gestureRecognizers else { return  }
        
        for gesture in gestureRecognizers {
            if gesture is UILongPressGestureRecognizer {
                gesture.require(toFail: longPressGesture)
            }
        }
        
        self.collectionView?.addGestureRecognizer(longPressGesture)
    }
    
    func removeGesture() {
        if longPressGesture != nil {
            if longPressGesture.view != nil {
                longPressGesture.view?.removeGestureRecognizer(longPressGesture)
            }
            
            longPressGesture = nil
        }
    }
    
    //MARK: - 长按手势方法
    @objc func longPressAction(longPress:UILongPressGestureRecognizer) {
     
        let point = longPress.location(in: self.collectionView)
        let superPoint = longPress.location(in: self.collectionView?.superview)
        
        
        switch longPress.state {
        case .began:
            movingIndexPath = self.collectionView?.indexPathForItem(at: point)
            if let movingIndexPath = movingIndexPath {
               beginMoveItemAtIndex(indexPath: movingIndexPath)
            }
            
            self.invalidateLayout()
            
        case .changed:
            snapshotView.center = superPoint
            guard let sourceIndexPath = movingIndexPath else { return }
            guard let destinationIndexPath = self.collectionView?.indexPathForItem(at: superPoint) else { return }

            movedItem(sourceIndexPath: sourceIndexPath, destinationIndexPath: destinationIndexPath)
            
        case .ended:
          endMoveItem()
          invalidateLayout()
            
        default:
          print("")
        }
    }
    
   
    func beginMoveItemAtIndex(indexPath:IndexPath) {
        
        if self.dataSource?.collectionView?(self.collectionView, canMove: indexPath) == false {
            movingIndexPath = nil
            return
        }
        
        self.delegate?.collectionView?(self.collectionView, willBeginDragging: self, indexPath: indexPath)
        
        guard let cell = self.collectionView?.cellForItem(at: indexPath) else { return }

        snapshotView = cell.snapshotView(afterScreenUpdates: false)
        snapshotView.center = cell.center
        originCellCenter = cell.center
        self.collectionView?.addSubview(snapshotView)
        
        self.delegate?.collectionView?(self.collectionView, didBeginDragging: self, indexPath: indexPath)

        
    }
    
    func movedItem(sourceIndexPath:IndexPath,destinationIndexPath:IndexPath) {
        if destinationIndexPath == sourceIndexPath  {
            return
        }
        
        if self.dataSource?.collectionView?(self.collectionView, canMove: sourceIndexPath, destinationIndexPath: destinationIndexPath) == false {
            return
        }
        
        self.dataSource?.collectionView?(self.collectionView, willMove:sourceIndexPath, destinationIndexPath: destinationIndexPath)
        
        movingIndexPath = destinationIndexPath
        
        self.collectionView?.performBatchUpdates({
            self.collectionView?.deleteItems(at: [sourceIndexPath])
            self.collectionView?.insertItems(at: [destinationIndexPath])
        }, completion: { (finished) in
            
            if finished == true{
               self.dataSource?.collectionView?(self.collectionView, didMove:sourceIndexPath, destinationIndexPath: destinationIndexPath)
            }

        })
    }
    
    func endMoveItem() {
      
       guard let _movingIndexPath = movingIndexPath else { return  }
        
        self.delegate?.collectionView?(self.collectionView, willEndDragging: self, indexPath: _movingIndexPath)
        
        let attributes = self.layoutAttributesForItem(at: _movingIndexPath)
        snapshotView.center = attributes?.center ?? CGPoint.zero
        
        self.delegate?.collectionView?(self.collectionView, didEndDragging: self, indexPath: _movingIndexPath)
        
        snapshotView.removeAllSubviews()
        snapshotView = nil
        movingIndexPath = nil
        originCellCenter = CGPoint.zero

        
    }
    
   
}
